#pragma once
#include <ConnectivityPort/can_traits.h>
#include <etl/deque.h>
#include <rtos/semaphore.h>
#include <main.h>
#include <signal_os.h>

namespace os {

template <size_t canId>
class Can final : public CanTraits {
    static_assert(is_HAS_CAN_enabled<canId>(), "The can port is disabled.");

    CanHandler* handle_ = get_CanHandler<canId>();

    Can() {
        CanRegisterRxFifo0Callback(handle_, fifo0CallbackWarper);
        CanRegisterRxFifo1Callback(handle_, fifo1CallbackWarper);
        CanRegisterTxEventFifoCallback(handle_, txEventFifoCallbackWarper);
    }

    etl::deque<Data, 12> queue_{};
    Semaphore<SemaphoreType::MUTEX> mutex_;

public:
    Can(Can const&) = delete;
    Can& operator=(Can const&) = delete;

    static Can& instance() {
        static Can instance;
        return instance;
    }

    [[nodiscard]] CanHandler* getHandlePtr() const { return handle_; }

    Signal<CanRxHeader*, uint8_t*> onReceive_{};

    void receive_fifo0_callback() {
        do {
            CanGetRxMessage(handle_, FDCAN_RX_FIFO0, &rxHeader_, rx_data_);
            onReceive_.emit(&rxHeader_, rx_data_);
        } while (CanGetFifi0RxMsgCount(handle_));
    }

    void receive_fifo1_callback() {
        CanGetRxMessage(handle_, FDCAN_RX_FIFO1, &rxHeader_, rx_data_);
        onReceive_.emit(&rxHeader_, rx_data_);
    }
    HAL_StatusTypeDef transmit_call_back() {
        CanGetTxEvent(handle_, txEvent_);
        while (CanGetTxFifoFreeLevel(handle_) and !queue_.empty()) {
            auto& [header, buffer] = queue_.front();
            transmit_imp(handle_, &header, buffer);
            queue_.pop_front();
        }
        return HAL_OK;
    }
    static void fifo0CallbackWarper(FDCAN_HandleTypeDef*, uint32_t) { instance().receive_fifo0_callback(); }

    static void fifo1CallbackWarper(FDCAN_HandleTypeDef*, uint32_t) { instance().receive_fifo1_callback(); }

    static void txEventFifoCallbackWarper(FDCAN_HandleTypeDef*, uint32_t) { instance().transmit_call_back(); }

    HAL_StatusTypeDef transmit(CanTxHeader tx_header, uint8_t const* tx_buffer) {
        Lock _{mutex_};
        if (CanGetTxFifoFreeLevel(handle_)) {
            auto const s = transmit_imp(handle_, &tx_header, tx_buffer);
            return s;
        }
        if (queue_.full()) {
            queue_.pop_front();
        }
        queue_.emplace_back(tx_header, tx_buffer);
        return HAL_OK;
    }
};

/**
 * @brief 概念约束Can类型
 */
template <typename T>
concept CanPort = requires { []<size_t ID>(Can<ID>*) {}(static_cast<T*>(nullptr)); };
}  // namespace os